/*******************************************************************************
 *
 * Copyright (c) 2008-2011 ayound@gmail.com, guwei8576431@gmail.com
 * This program and the accompanying materials
 * are made available under the terms of the GNU GENERAL PUBLIC LICENSE 
 * which accompanies this distribution, and is available at
 * http://dev.perl.org/licenses/gpl1.html
 * All rights reserved.
 * 
 * Created on 2009-12-6
 *******************************************************************************/

package org.cgtw.scriptprofile.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;

import org.cgtw.scriptprofile.server.listener.IProfileDataChangeListener;
import org.cgtw.scriptprofile.server.model.ProfileData;
import org.cgtw.scriptprofile.server.model.ProfileItem;
import org.cgtw.scriptprofile.ui.message.ScriptProfileMessages;
import org.cgtw.scriptprofile.widget.AbstractTreeTableModel;
import org.cgtw.scriptprofile.widget.JTreeTable;
import org.cgtw.scriptprofile.widget.TreeTableModel;

public class TreeViewPane extends JPanel implements IProfileDataChangeListener {

	private JTreeTable treeTable;

	private JScrollPane scrollPane;
	
	private JButton button;
	
	private boolean view_state = false;

	public TreeViewPane() {
		super();
		
		BorderLayout layout = new BorderLayout();
		this.setLayout(layout);
		scrollPane = new JScrollPane();
		button = new JButton(ScriptProfileMessages.getString("ScriptProfileMainFrame.ChangeView"));
		button.addMouseListener(new MouseListener(){
			public void mouseClicked(MouseEvent e) {
				if(treeTable != null){
					TreeViewPane.this.remove(treeTable);
					ProfileTreeItemBean root = new ProfileTreeItemBean();
					root.setFunction(ScriptProfileMessages
							.getString("ScriptProfileMainFrame.FunctionStack"));
					
					view_state = !view_state;
					if(view_state){
						TreeTableModel model = new ProfileTreeTableModel(root,
								ProfileTreeItemTranslator.translator2());

						treeTable = new ProfileTreeTableViewer2(model);
					}else{
						TreeTableModel model = new ProfileTreeTableModel(root,
								ProfileTreeItemTranslator.translator());
						treeTable = new ProfileTreeTableViewer(model);
					}
					
					scrollPane.getViewport().add(treeTable);
				}
			}
			public void mouseEntered(MouseEvent e) {}
			public void mouseExited(MouseEvent e) {}
			public void mousePressed(MouseEvent e) {}
			public void mouseReleased(MouseEvent e) {}
		});
		
		
		this.add(scrollPane, BorderLayout.CENTER);
		this.add(button, BorderLayout.SOUTH);
	}

	public void load() {
		ProfileTreeItemBean root = new ProfileTreeItemBean();
		root.setFunction(ScriptProfileMessages
				.getString("ScriptProfileMainFrame.FunctionStack"));
		TreeTableModel model = new ProfileTreeTableModel(root,
				ProfileTreeItemTranslator.translator());

		treeTable = new ProfileTreeTableViewer(model);	    
		this.scrollPane.getViewport().add(treeTable);
	}

	public void change() {
		view_state = false;
		if (treeTable != null) {
			this.remove(treeTable);
		}
		this.load();
	}

}

class ProfileTreeTableModel extends AbstractTreeTableModel {

	private String columnNames[] = new String[] {
			ScriptProfileMessages
					.getString("ScriptProfileMainFrame.FunctionStack"),
			ScriptProfileMessages
					.getString("ScriptProfileMainFrame.FunctionTime") };

	public ProfileTreeTableModel(Object root, List<ProfileTreeItemBean> items) {
		super(root);
		init(items);
	}

	public void init(List<ProfileTreeItemBean> list) {
		if (list != null) {
			for (ProfileTreeItemBean bean : list) {
				((ProfileTreeItemBean) this.getRoot()).addChild(bean);
			}
		}

	}

	public int getColumnCount() {
		return columnNames.length;
	}

	public String getColumnName(int column) {
		return columnNames[column];
	}

	public Class getColumnClass(int column) {
		if (column == 0) {
			return TreeTableModel.class;
		} else {
			return String.class;
		}
	}

	public Object getValueAt(Object node, int column) {
		if (node instanceof ProfileTreeItemBean) {
			ProfileTreeItemBean bean = (ProfileTreeItemBean) node;
			if (column == 0) {
				return bean.getFunction();
			} else if (column == 1) {
				return ProfileTreeItemTranslator.begin + ":" + ProfileTreeItemTranslator.end + ":" + bean.getStart() + ":" + bean.getEnd();
//				return bean.getEnd() - bean.getStart();
			}
		}
		return null;
	}

	public Object getChild(Object parent, int index) {
		if (parent instanceof ProfileTreeItemBean) {
			ProfileTreeItemBean bean = (ProfileTreeItemBean) parent;
			List childs = bean.getChild();
			if (childs == null) {
				return null;
			} else {
				return childs.get(index);
			}
		}
		return null;
	}

	public int getChildCount(Object parent) {
		if (parent instanceof ProfileTreeItemBean) {
			ProfileTreeItemBean bean = (ProfileTreeItemBean) parent;
			List childs = bean.getChild();
			if (childs == null) {
				return 0;
			} else {
				return childs.size();
			}
		}
		return 0;
	}

}

//TODO
class ProfileTreeTableViewer extends JTreeTable {

	public ProfileTreeTableViewer(TreeTableModel treeTableModel) {
		super(treeTableModel);
		this.setDefaultRenderer(this.getColumnClass(1), new TreeRender());
	}
	
	private class TreeRender extends DefaultTableCellRenderer {
		
		@Override
		public Component getTableCellRendererComponent(JTable table, Object value,
				boolean isSelected, boolean hasFocus, int row, int column) {
			
			if(column == 1){
				Colors colors = new Colors(value);
				return colors;
			}
			
			return super.getTableCellRendererComponent(table, value, isSelected,
					hasFocus, row, column);
		}
	}
	

}

class ProfileTreeTableViewer2 extends JTreeTable {

	public ProfileTreeTableViewer2(TreeTableModel treeTableModel) {
		super(treeTableModel);
		this.setDefaultRenderer(this.getColumnClass(1), new TreeRender2());
	}
	
	private class TreeRender2 extends DefaultTableCellRenderer {
		
		@Override
		public Component getTableCellRendererComponent(JTable table, Object value,
				boolean isSelected, boolean hasFocus, int row, int column) {
			
			if(column == 1){
				Colors2 colors = new Colors2(value);
				return colors;
			}
			
			return super.getTableCellRendererComponent(table, value, isSelected,
					hasFocus, row, column);
		}
	}
	

}


class ProfileTreeItemTranslator {
	
	public static long begin;

	public static long end;
	
	public static List<ProfileTreeItemBean> translator2() {
		begin = 0;
		end = 0;

		List<ProfileTreeItemBean> returnList = new ArrayList<ProfileTreeItemBean>();
		Map<Integer, ProfileItem> map = ProfileData.getItems();
		Integer[] rootIndex = ProfileData.getRootIndex();

		if (map != null && !map.isEmpty()) {
			if (rootIndex != null) {
				for (Integer index : rootIndex) {
					ProfileItem item = map.get(index);
					if (item != null) {
						ProfileTreeItemBean childBean = new ProfileTreeItemBean();
						childBean.setFunction(item.getFuncName());
						childBean.setStart(item.getStart());
						childBean.setEnd(item.getEnd());
						returnList.add(childBean);

						if (begin == 0 || item.getStart() < begin) {
							begin = item.getStart();
						}
						if (item.getEnd() > end) {
							end = item.getEnd();
						}

						Map<Integer, Integer[]> relationMap = ProfileData
								.getRelations();
						if (relationMap != null) {
							Integer[] relations = relationMap.get(index);
							ProfileTreeItemTranslator.addChild2(map,
									relationMap, childBean, relations);
						}
					}

				}
			}

		}

		return returnList;
	}

	private static void addChild2(Map<Integer, ProfileItem> items,
			Map<Integer, Integer[]> relationMap, ProfileTreeItemBean bean,
			Integer[] relations) {
		if (relations == null || relations.length == 0) {
			return;
		}
		for (Integer relation : relations) {
			ProfileItem item = items.get(relation);
			if (item != null) {
				ProfileTreeItemBean childBean = new ProfileTreeItemBean();
				childBean.setFunction(item.getFuncName());
				childBean.setStart(item.getStart());
				childBean.setEnd(item.getEnd());
				
				
				if(bean.getChild()==null || bean.getChild().size()==0){
					bean.addChild(childBean, 0);
				}else{
					long excuteTime = item.getEnd() - item.getStart();
					List <ProfileTreeItemBean>list = bean.getChild();
					
					for(int i=0;i<list.size();i++){
						long time = list.get(i).getEnd() - list.get(i).getStart();
						if(excuteTime >= time){
							bean.addChild(childBean, i);
							break;
						}
						if(excuteTime < time){
							if(i == list.size()-1){
								bean.addChild(childBean, i+1);
								break;
							}
							continue;
						}
					}
				}
				

				if (begin == 0 || item.getStart() < begin) {
					begin = item.getStart();
				}
				if (item.getEnd() > end) {
					end = item.getEnd();
				}

				Integer[] childRelations = relationMap.get(relation);
				ProfileTreeItemTranslator.addChild2(items, relationMap,
						childBean, childRelations);
			}
		}
	}
	

	public static List<ProfileTreeItemBean> translator() {
		begin = 0;
		end = 0;

		List<ProfileTreeItemBean> returnList = new ArrayList<ProfileTreeItemBean>();
		Map<Integer, ProfileItem> map = ProfileData.getItems();
		Integer[] rootIndex = ProfileData.getRootIndex();

		if (map != null && !map.isEmpty()) {
			if (rootIndex != null) {
				for (Integer index : rootIndex) {
					ProfileItem item = map.get(index);
					if (item != null) {
						ProfileTreeItemBean childBean = new ProfileTreeItemBean();
						childBean.setFunction(item.getFuncName());
						childBean.setStart(item.getStart());
						childBean.setEnd(item.getEnd());
						returnList.add(childBean);

						if (begin == 0 || item.getStart() < begin) {
							begin = item.getStart();
						}
						if (item.getEnd() > end) {
							end = item.getEnd();
						}

						Map<Integer, Integer[]> relationMap = ProfileData
								.getRelations();
						if (relationMap != null) {
							Integer[] relations = relationMap.get(index);
							ProfileTreeItemTranslator.addChild(map,
									relationMap, childBean, relations);
						}
					}

				}
			}

		}

		return returnList;
	}

	private static void addChild(Map<Integer, ProfileItem> items,
			Map<Integer, Integer[]> relationMap, ProfileTreeItemBean bean,
			Integer[] relations) {
		if (relations == null || relations.length == 0) {
			return;
		}
		for (Integer relation : relations) {
			ProfileItem item = items.get(relation);
			if (item != null) {
				ProfileTreeItemBean childBean = new ProfileTreeItemBean();
				childBean.setFunction(item.getFuncName());
				childBean.setStart(item.getStart());
				childBean.setEnd(item.getEnd());
				bean.addChild(childBean);

				if (begin == 0 || item.getStart() < begin) {
					begin = item.getStart();
				}
				if (item.getEnd() > end) {
					end = item.getEnd();
				}

				Integer[] childRelations = relationMap.get(relation);
				ProfileTreeItemTranslator.addChild(items, relationMap,
						childBean, childRelations);
			}
		}
	}

}

class ProfileTreeItemBean {
	private String function;

	private long start;

	private long end;

	private List<ProfileTreeItemBean> child = new ArrayList<ProfileTreeItemBean>();

	/**
	 * @return Returns the child.
	 */
	public List<ProfileTreeItemBean> getChild() {
		return child;
	}

	/**
	 * @param child The child to set.
	 */
	//	public void setChild(List<ProfileTreeItemBean> child) {
	//		this.child = child;
	//	}
	public void addChild(ProfileTreeItemBean bean) {
		child.add(bean);
	}
	
	public void addChild(ProfileTreeItemBean bean, int index){
		child.add(index, bean);
	}

	/**
	 * @return Returns the function.
	 */
	public String getFunction() {
		return function;
	}

	/**
	 * @param function The function to set.
	 */
	public void setFunction(String function) {
		this.function = function;
	}

	/**
	 * @return Returns the end.
	 */
	public long getEnd() {
		return end;
	}

	/**
	 * @param end The end to set.
	 */
	public void setEnd(long end) {
		this.end = end;
	}

	/**
	 * @return Returns the start.
	 */
	public long getStart() {
		return start;
	}

	/**
	 * @param start The start to set.
	 */
	public void setStart(long start) {
		this.start = start;
	}
	
	public String toString(){
		if(this.getStart() == 0){
			return this.getFunction();
		}else{
			return (this.getEnd()-this.getStart())*100/(ProfileTreeItemTranslator.end-ProfileTreeItemTranslator.begin) + "% " + this.getFunction();
		}
	}

}

class Colors extends JLabel implements Serializable {
	private Color emptyColor = Color.white; 
	private Color excuteColor = Color.blue;
	private String value;

	public Colors(Object value2) {
		this.value = (String) value2;
		change();
	}

	public void change() {
		repaint();
	}

	public void paint(Graphics g) {
		Dimension d = getSize();
		int h = d.height;
		int w = d.width;
		
		if(value != null){
			String []valueArray = value.split(":");
			if(Long.parseLong(valueArray[0]) == 0){
				g.setColor(emptyColor);
				g.fillRect(0, 0, w - 1, h - 1);
				return;
			}
			long total = Long.parseLong(valueArray[1]) - Long.parseLong(valueArray[0]);
			long emptyLeft = Long.parseLong(valueArray[2]) - Long.parseLong(valueArray[0]);
			long excute = Long.parseLong(valueArray[3]) - Long.parseLong(valueArray[2]);
			
			int left = ((Long)(emptyLeft * w / total)).intValue();
			int middle = ((Long)(excute * w / total)).intValue();
			
			g.setColor(emptyColor);
			g.fillRect(0, 0, left - 1, h - 1);
			g.setColor(excuteColor);
			g.fillRect(left, 0, left + middle - 1, h - 1);
			g.setColor(emptyColor);
			g.fillRect(left + middle, 0, w - 1, h - 1);

		}
		
		
	}
}

class Colors2 extends JLabel implements Serializable {
	private Color emptyColor = Color.white; 
	private Color excuteColor = Color.blue;
	private String value;

	public Colors2(Object value2) {
		this.value = (String) value2;
		change();
	}

	public void change() {
		repaint();
	}

	public void paint(Graphics g) {
		Dimension d = getSize();
		int h = d.height;
		int w = d.width;
		
		if(value != null){
			String []valueArray = value.split(":");
			if(Long.parseLong(valueArray[0]) == 0){
				g.setColor(emptyColor);
				g.fillRect(0, 0, w - 1, h - 1);
				return;
			}
			long total = Long.parseLong(valueArray[1]) - Long.parseLong(valueArray[0]);
			//long emptyLeft = Long.parseLong(valueArray[2]) - Long.parseLong(valueArray[0]);
			long excute = Long.parseLong(valueArray[3]) - Long.parseLong(valueArray[2]);
			
			//int left = ((Long)(emptyLeft * w / total)).intValue();
			int middle = ((Long)(excute * w / total)).intValue();
			
			g.setColor(excuteColor);
			g.fillRect(0, 0, middle - 1, h - 1);
			g.setColor(emptyColor);
			g.fillRect(middle, 0, w - 1, h - 1);

		}
		
		
	}
}
